package com.github.cakin.shiro.chapter2.authenticator.strategy;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.pam.AbstractAuthenticationStrategy;
import org.apache.shiro.realm.Realm;

import java.util.Collection;

/**
 * @className: OnlyOneAuthenticatorStrategy
 * @description: 只能成功认证一次，如果成功认证多次就抛出异常
 * @date: 2020/5/16
 * @author: cakin
 */
public class OnlyOneAuthenticatorStrategy extends AbstractAuthenticationStrategy {
    /**
     * 功能描述：在所有Realm验证之前调用
     *
     * @param realms 代表多Realm
     * @param token  认证token
     * @return AuthenticationInfo 认证信息
     * @author cakin
     * @date 2020/5/16
     */
    @Override
    public AuthenticationInfo beforeAllAttempts( Collection<? extends Realm> realms, AuthenticationToken token ) throws AuthenticationException {
        return new SimpleAuthenticationInfo(); // 返回一个权限的认证信息
    }

    /**
     * 功能描述：在每个Realm之前调用
     *
     * @param realm 代表认证要使用的Realm
     * @param token 用户token
     * @param aggregate 之前合并的认证信息
     * @return AuthenticationInfo 返回之前合并的
     * @author cakin
     * @date 2020/5/16
     */
    @Override
    public AuthenticationInfo beforeAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo aggregate ) throws AuthenticationException {
        return aggregate; // 返回之前合并的
    }

    /**
     * 功能描述：在每个Realm之后调用
     *
     * @param realm 代表认证要使用的Realm
     * @param token 用户token
     * @param singleRealmInfo 本次匹配的认证信息
     * @param aggregateInfo 之前合并的认证信息
     * @return AuthenticationInfo 将之前的合并结果和本次匹配的认证结果进一步合并，得到最终的合并结果
     * @author cakin
     * @date 2020/5/16
     */
    @Override
    public AuthenticationInfo afterAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo,
                                            AuthenticationInfo aggregateInfo, Throwable t ) throws AuthenticationException {
        AuthenticationInfo info;
        // 没有匹配的认证信息
        if (singleRealmInfo == null) {
            info = aggregateInfo;
        } else { // 有匹配的认证信息
            if (aggregateInfo == null) { // 之前合并的结果为空
                info = singleRealmInfo;
            } else {
                info = merge(singleRealmInfo, aggregateInfo); // 将本次匹配的认证结果和之前合并的认证结果进一步合并
                // 只能匹配一次，否则抛出异常
                if (info.getPrincipals().getRealmNames().size() > 1) {
                    System.out.println(info.getPrincipals().getRealmNames());
                    throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] "
                            + "could not be authenticated by any configured realms.  Please ensure that only one realm can "
                            + "authenticate these tokens.");
                }
            }
        }


        return info;
    }

    /**
     * 功能描述：在所有Realm之后调用
     *
     * @param token 用户token
     * @param aggregate 合并后的认证信息
     * @return AuthenticationInfo 合并后的认证信息
     * @author cakin
     * @date 2020/5/16
     * @description:
     */
    @Override
    public AuthenticationInfo afterAllAttempts( AuthenticationToken token, AuthenticationInfo aggregate ) throws AuthenticationException {
        return aggregate;
    }
}
